import type { IFunction, IObject } from '@/types/interface';

/**
 * 获取对象下的字段值
 * @param record 对象
 * @param key 字段路径，如 'a.b.c'
 * @param defaultValue 默认值
 * @returns 字段值
 */
export function getValueByKeys(record: IObject = {}, key: string, defaultValue?: unknown): any {
  const keys = key.split('.');
  for (let i = 0; i < keys.length; i++) {
    record = record[keys[i]] || (i === keys.length - 1 ? defaultValue : {});
  }
  return record || defaultValue;
}

/**
 * 数组转对象
 * @param rs 集合
 * @param key 需要转换目标key的名称
 * @param render 渲染函数
 * @returns 转换后的对象
 */
export function arrayToObject(rs: any[] = [], key: string | IFunction, render?: IFunction): IObject {
  const o: IObject = {};
  rs.forEach((x) => {
    o[typeof key === 'function' ? key(x) : x[key]] = render ? render(x) : x;
  });
  return o;
}

/**
 * 数组转换格式
 * @param rs 数组
 * @param render 渲染函数
 * @returns 转换后的数组
 */
export function arrayToKeyValueArray(rs: any[] = [], render?: IFunction): any[] {
  return rs.map(x => (render ? render(x) : typeof x === 'object' ? x : { label: x, value: x }));
}

/**
 * 是否只null和undefined值
 * @param vl 值
 * @returns 是否为null或undefined
 */
export function isNullOrUndefined(vl: unknown): boolean {
  return vl === null || typeof vl === 'undefined';
}

/**
 * 是否外链
 * @param path 路径
 * @returns 是否为外链
 */
export function isExternalLink(path: string): boolean {
  return /^(?:https?:|\/\/|mailto:|tel:|\{\{\s?ApiUrl\s?\}\})/.test(path);
}

/**
 * 复制到剪贴板
 * @param value 要复制的值
 */
export function copyToClipboard(value: string): void {
  const transfer = document.createElement('textarea');
  document.body.appendChild(transfer);
  transfer.value = value;
  transfer.focus();
  transfer.select();
  if (document.execCommand('copy')) {
    document.execCommand('copy');
  }
  transfer.blur();
  document.body.removeChild(transfer);
}

/**
 * 检查是否有权限
 * @param permission 权限数组
 * @param key 权限键
 * @returns 是否有权限
 */
export function checkPermission(permission: string[], key: string): boolean {
  return permission.includes(key);
}

/**
 * 获取uuid
 * @returns UUID字符串
 */
export function getUuid(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    const v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

/**
 * 邮箱验证
 * @param s 邮箱字符串
 * @returns 是否为有效邮箱
 */
export function isEmail(s: string): boolean {
  return /^[\w.-]+@[\w-]+(?:\.[\w-]{2,3}){1,2}$/.test(s);
}

/**
 * 手机号码验证
 * @param s 手机号字符串
 * @returns 是否为有效手机号
 */
export function isMobile(s: string): boolean {
  return /^1\d{10}$/.test(s);
}

/**
 * 电话号码验证
 * @param s 电话号字符串
 * @returns 是否为有效电话号
 */
export function isPhone(s: string): boolean {
  return /^(?:\d{3,4}-)?\d{7,8}$/.test(s);
}

/**
 * URL地址验证
 * @param s URL字符串
 * @returns 是否为有效URL
 */
export function isURL(s: string): boolean {
  return /^https?:\/\/.+/.test(s);
}

/**
 * 正整数验证
 * @param s 数字字符串
 * @returns 是否为正整数
 */
export function isNumber(s: string): boolean {
  return /^\+?[1-9]\d*$/.test(s);
}

/**
 * 获取字典数据列表
 * @param list 字典列表
 * @param dictType 字典类型
 * @returns 字典数据列表
 */
export function getDictDataList(list: IObject[], dictType?: string): IObject[] {
  const type = list.find((element: IObject) => element.dictType === dictType);
  if (type) {
    return type.dataList;
  }
  else {
    return [];
  }
}

/**
 * 获取字典名称
 * @param list 字典列表
 * @param dictType 字典类型
 * @param dictValue 字典值
 * @returns 字典标签
 */
export function getDictLabel(list: IObject[], dictType: string, dictValue: number): string | number {
  const type = list.find((element: IObject) => element.dictType === dictType);
  if (type) {
    const val = type.dataList.find((element: IObject) => element.dictValue === `${dictValue}`);
    if (val) {
      return val.dictLabel;
    }
    else {
      return dictValue;
    }
  }
  else {
    return dictValue;
  }
}

/**
 * 获取svg图标(id)列表
 * @returns 图标ID列表
 */
export function getIconList(): string[] {
  const rs: string[] = [];
  const list = document.querySelectorAll('svg symbol');
  for (let i = 0; i < list.length; i++) {
    rs.push(list[i].id);
  }
  return rs;
}

/**
 * 树形数据转换
 * @param data 数据数组
 * @param id ID字段名
 * @param pid 父ID字段名
 * @returns 树形结构数据
 */
export function treeDataTranslate(data: IObject[], id?: string, pid?: string): IObject[] {
  const res: IObject[] = [];
  const temp: IObject = {};
  id = id || 'id';
  pid = pid || 'pid';
  for (let i = 0; i < data.length; i++) {
    temp[data[i][id]] = data[i];
  }
  for (let k = 0; k < data.length; k++) {
    if (!temp[data[k][pid]] || data[k][id] === data[k][pid]) {
      res.push(data[k]);
      continue;
    }
    if (!temp[data[k][pid]].children) {
      temp[data[k][pid]].children = [];
    }
    temp[data[k][pid]].children.push(data[k]);
    data[k]._level = (temp[data[k][pid]]._level || 0) + 1;
  }
  return res;
}

/**
 * 防抖函数
 * @param func 要防抖的函数
 * @param wait 等待时间（毫秒）
 * @param options 选项
 * @returns 防抖后的函数
 */
export function useDebounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number,
  options: { leading?: boolean; trailing?: boolean } = {}
): T {
  const { leading = false, trailing = true } = options;
  let timeout: NodeJS.Timeout | null = null;
  let lastCallTime: number | null = null;
  let lastInvokeTime = 0;
  let lastArgs: Parameters<T> | null = null;
  let lastThis: any = null;
  let result: ReturnType<T>;

  function invokeFunc(time: number) {
    const args = lastArgs!;
    const thisArg = lastThis;
    lastArgs = lastThis = null;
    lastInvokeTime = time;
    result = func.apply(thisArg, args);
    return result;
  }

  function leadingEdge(time: number) {
    lastInvokeTime = time;
    timeout = setTimeout(timerExpired, wait);
    return leading ? invokeFunc(time) : result;
  }

  function remainingWait(time: number) {
    const timeSinceLastCall = time - lastCallTime!;
    const timeSinceLastInvoke = time - lastInvokeTime;
    const timeWaiting = wait - timeSinceLastCall;
    return timeWaiting;
  }

  function shouldInvoke(time: number) {
    const timeSinceLastCall = time - lastCallTime!;
    const timeSinceLastInvoke = time - lastInvokeTime;
    return (
      lastCallTime === null ||
      timeSinceLastCall >= wait ||
      timeSinceLastCall < 0 ||
      timeSinceLastInvoke >= wait
    );
  }

  function timerExpired() {
    const time = Date.now();
    if (shouldInvoke(time)) {
      return trailingEdge(time);
    }
    timeout = setTimeout(timerExpired, remainingWait(time));
  }

  function trailingEdge(time: number) {
    timeout = null;
    if (trailing && lastArgs) {
      return invokeFunc(time);
    }
    lastArgs = lastThis = null;
    return result;
  }

  function cancel() {
    if (timeout !== null) {
      clearTimeout(timeout);
    }
    lastInvokeTime = 0;
    lastArgs = lastCallTime = lastThis = timeout = null;
  }

  function flush() {
    return timeout === null ? result : trailingEdge(Date.now());
  }

  function debounced(this: any, ...args: Parameters<T>) {
    const time = Date.now();
    const isInvoking = shouldInvoke(time);

    lastArgs = args;
    lastThis = this;
    lastCallTime = time;

    if (isInvoking) {
      if (timeout === null) {
        return leadingEdge(lastCallTime);
      }
      timeout = setTimeout(timerExpired, wait);
      return leading ? invokeFunc(lastCallTime) : result;
    }
    if (timeout === null) {
      timeout = setTimeout(timerExpired, wait);
    }
    return result;
  }

  debounced.cancel = cancel;
  debounced.flush = flush;
  return debounced as T;
}
